1   /*
2    * Copyright (C) 2012 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.reflect;
18  
19  import com.google.common.annotations.Beta;
20  import com.google.common.collect.ForwardingMap;
21  import com.google.common.collect.ImmutableMap;
22  
23  import java.util.Map;
24  
25  /**
26   * A type-to-instance map backed by an {@link ImmutableMap}. See also {@link
27   * MutableTypeToInstanceMap}.
28   *
29   * @author Ben Yu
30   * @since 13.0
31   */
32  @Beta
33  public final class ImmutableTypeToInstanceMap<B> extends ForwardingMap<TypeToken<? extends B>, B>
34      implements TypeToInstanceMap<B> {
35  
36    /** Returns an empty type to instance map. */
37    public static <B> ImmutableTypeToInstanceMap<B> of() {
38      return new ImmutableTypeToInstanceMap<B>(ImmutableMap.<TypeToken<? extends B>, B>of());
39    }
40  
41    /** Returns a new builder. */
42    public static <B> Builder<B> builder() {
43      return new Builder<B>();
44    }
45  
46    /**
47     * A builder for creating immutable type-to-instance maps. Example:
48     * <pre>   {@code
49     *
50     *   static final ImmutableTypeToInstanceMap<Handler<?>> HANDLERS =
51     *       ImmutableTypeToInstanceMap.<Handler<?>>builder()
52     *           .put(new TypeToken<Handler<Foo>>() {}, new FooHandler())
53     *           .put(new TypeToken<Handler<Bar>>() {}, new SubBarHandler())
54     *           .build();}</pre>
55     *
56     * <p>After invoking {@link #build()} it is still possible to add more entries
57     * and build again. Thus each map generated by this builder will be a superset
58     * of any map generated before it.
59     *
60     * @since 13.0
61     */
62    @Beta
63    public static final class Builder<B> {
64      private final ImmutableMap.Builder<TypeToken<? extends B>, B> mapBuilder
65          = ImmutableMap.builder();
66  
67      private Builder() {}
68  
69      /**
70       * Associates {@code key} with {@code value} in the built map. Duplicate
71       * keys are not allowed, and will cause {@link #build} to fail.
72       */
73      public <T extends B> Builder<B> put(Class<T> key, T value) {
74        mapBuilder.put(TypeToken.of(key), value);
75        return this;
76      }
77  
78      /**
79       * Associates {@code key} with {@code value} in the built map. Duplicate
80       * keys are not allowed, and will cause {@link #build} to fail.
81       */
82      public <T extends B> Builder<B> put(TypeToken<T> key, T value) {
83        mapBuilder.put(key.rejectTypeVariables(), value);
84        return this;
85      }
86  
87      /**
88       * Returns a new immutable type-to-instance map containing the entries
89       * provided to this builder.
90       *
91       * @throws IllegalArgumentException if duplicate keys were added
92       */
93      public ImmutableTypeToInstanceMap<B> build() {
94        return new ImmutableTypeToInstanceMap<B>(mapBuilder.build());
95      }
96    }
97  
98    private final ImmutableMap<TypeToken<? extends B>, B> delegate;
99  
100   private ImmutableTypeToInstanceMap(ImmutableMap<TypeToken<? extends B>, B> delegate) {
101     this.delegate = delegate;
102   }
103 
104   @Override public <T extends B> T getInstance(TypeToken<T> type) {
105     return trustedGet(type.rejectTypeVariables());
106   }
107 
108   /**
109    * Guaranteed to throw an exception and leave the map unmodified.
110    *
111    * @throws UnsupportedOperationException always
112    */
113   @Override public <T extends B> T putInstance(TypeToken<T> type, T value) {
114     throw new UnsupportedOperationException();
115   }
116 
117   @Override public <T extends B> T getInstance(Class<T> type) {
118     return trustedGet(TypeToken.of(type));
119   }
120 
121   /**
122    * Guaranteed to throw an exception and leave the map unmodified.
123    *
124    * @throws UnsupportedOperationException always
125    */
126   @Override public <T extends B> T putInstance(Class<T> type, T value) {
127     throw new UnsupportedOperationException();
128   }
129 
130   @Override protected Map<TypeToken<? extends B>, B> delegate() {
131     return delegate;
132   }
133 
134   @SuppressWarnings("unchecked") // value could not get in if not a T
135   private <T extends B> T trustedGet(TypeToken<T> type) {
136     return (T) delegate.get(type);
137   }
138 }